﻿using System.Collections;
using System.Collections.Generic;

using UnityEngine;
using UnityEngine.UI;

[RequireComponent(typeof(ScrollRect))]
public class UGUIScrollView : MonoBehaviour
{
    public int ItemCount
    {
        get
        {
            return items.Count;
        }
    }

    public ScrollRect ScrollRect
    {
        get
        {
            return scrollRect;
        }
    }

    public RectTransform Viewport
    {
        get
        {
            return viewPort;
        }
    }

    public RectTransform Content
    {
        get
        {
            return content;
        }
    }

    [Header("Basic Properties")]
    [SerializeField]
    protected ScrollRect scrollRect;

    [SerializeField]
    protected RectTransform viewPort;

    [SerializeField]
    protected RectTransform content;

    [Header("Extended Properties")]
    [SerializeField]
    protected GameObject indicatorUp;

    [SerializeField]
    protected GameObject indicatorDown;

    [SerializeField]
    protected GameObject indicatorLeft;

    [SerializeField]
    protected GameObject indicatorRight;

    [Header("Debug")]
    [SerializeField]
    protected List<GameObject> items = new List<GameObject>();

    private RectTransform _cachedViewportTran;
    private RectTransform _cachedContentTran;

    void Start()
    {
        if (scrollRect == null)
        {
            scrollRect = GetComponent<ScrollRect>();
        }

        if (content == null)
        {
            if (scrollRect != null)
            {
                content = scrollRect.content;
            }
        }

        if (viewPort == null)
        {
            if (scrollRect != null)
            {
                viewPort = scrollRect.viewport;
            }
        }

        // 缓存组件
        if (content != null)
        {
            _cachedContentTran = content.GetComponent<RectTransform>();
        }
        if (viewPort != null)
        {
            _cachedViewportTran = viewPort.GetComponent<RectTransform>();
        }

        // 附加回调
        if (scrollRect != null)
        {
            scrollRect.onValueChanged.AddListener(HandleScrollRectValueChanged);
            HandleScrollRectValueChanged(new Vector2(scrollRect.horizontalNormalizedPosition, scrollRect.verticalNormalizedPosition));
        }
    }

    void OnDestroy()
    {
        // 移除回调
        if (scrollRect != null)
        {
            scrollRect.onValueChanged.RemoveListener(HandleScrollRectValueChanged);
            scrollRect = null;
        }

        viewPort = null;
        content = null;

        _cachedViewportTran = null;
        _cachedContentTran = null;
    }

    public void AddItem(GameObject go)
    {
        items.Add(go);

        go.transform.SetParent(content, false);
        go.transform.localScale = Vector3.one;
        go.SetActive(true);
    }

    public GameObject GetItem(int index)
    {
        return items[index];
    }

    public void RemoveItem(GameObject go, bool destroyItem = true)
    {
        items.Remove(go);

        if (destroyItem)
        {
            Destroy(go);
        }
    }

    public void RemoveItemAt(int i, bool destroyItem = true)
    {
        if (i >= 0 && i < items.Count)
        {
            var item = items[i];
            items.RemoveAt(i);

            if (destroyItem)
            {
                Destroy(item);
            }
        }
    }

    public void ClearItems(bool destroyItems = true)
    {
        foreach (GameObject go in items)
        {
            if (go != null && destroyItems)
            {
                Destroy(go);
            }
        }

        items.Clear();
    }

    private void HandleScrollRectValueChanged(Vector2 pos)
    {
        if (scrollRect != null && _cachedViewportTran != null && _cachedContentTran != null)
        {
            var viewportSize = _cachedViewportTran.rect;
            var contentSize = _cachedContentTran.rect;

            SetActiveInternal(indicatorLeft, scrollRect.horizontal && pos.x > 0.0f && viewportSize.width <= contentSize.width);
            SetActiveInternal(indicatorRight, scrollRect.horizontal && pos.x < 1.0f && viewportSize.width <= contentSize.width);
            SetActiveInternal(indicatorUp, scrollRect.vertical && pos.y < 1.0f && viewportSize.height <= contentSize.height);
            SetActiveInternal(indicatorDown, scrollRect.vertical && pos.y > 0.0f && viewportSize.height <= contentSize.height);
        }
    }

    private void SetActiveInternal(GameObject go, bool active)
    {
        if (go != null && go.activeSelf != active)
        {
            go.SetActive(active);
        }
    }
}